package org.api.result;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import org.api.result.config.EnableApiResultConfiguration;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Getter
public class R implements Serializable {

    private final int code;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private final String msg;

    @JsonIgnore
    private boolean encrypted;

    @JsonIgnore
    private HttpStatus httpStatus;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String desc;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Object data;

    @Getter(onMethod_ = @JsonAnyGetter)
    private Map<String, Object> params;

    private R(int code, HttpStatus httpStatus, Object data, String msg, String desc) {
        Assert.notNull(httpStatus, "httpStatus must not be null");
        this.httpStatus = httpStatus;
        this.code = code;
        this.msg = msg;
        this.data = data;
        if (httpStatus.isError()) {
            this.desc = desc;
        }
    }

    public static R of(int code, HttpStatus httpStatus, Object data, String msg, String desc) {
        return new R(code, httpStatus, data, msg, desc);
    }

    public static R of(IResultCode rc, Object data, String msg) {
        return of(rc.getCode(), rc.getHttpStatus(), data, msg, rc.getDesc());
    }

    public static R ok() {
        return ok((Object) null);
    }

    public static R ok(Page<?> page) {
        R r = ok(page.getContent(), null);
        r.set("totalElements", page.getTotalElements());
        r.set("totalPages", page.getTotalPages());
        r.set(EnableApiResultConfiguration.getPageParameter(), page.getNumber() + 1);
        r.set(EnableApiResultConfiguration.getSizeParameter(), page.getSize());
        r.set("first", page.isFirst());
        r.set("last", page.isLast());
        r.set("sort", page.getSort());
        return r;
    }

    public static R ok(Object data) {
        return ok(data, null);
    }

    public static R ok(Object data, String msg) {
        return of(RC.OK, data, msg);
    }


    /**
     * 服务器错误
     */
    public static R error() {
        return error("未知错误");
    }

    /**
     * 服务器错误
     */
    public static R error(String msg) {
        return error(RC.INTERNAL_SERVER_ERROR, msg);
    }

    /**
     * 服务器错误
     */
    public static R error(IResultCode rc) {
        return error(rc, null, rc.getDesc());
    }

    /**
     * 服务器错误
     */
    public static R error(IResultCode rc, String msg) {
        return error(rc, null, msg);
    }

    /**
     * 服务器错误
     */
    public static R error(IResultCode rc, Object data, String msg) {
        return of(rc, data, msg);
    }

    /**
     * 客户端错误
     */
    public static R fail() {
        return fail("错误请求");
    }

    /**
     * 客户端错误
     */
    public static R fail(String msg) {
        return fail(RC.BAD_REQUEST, msg);
    }

    /**
     * 客户端错误
     *
     * @param rc
     */
    public static R fail(IResultCode rc) {
        return fail(rc, rc.getDesc());
    }

    /**
     * 客户端错误
     */
    public static R fail(IResultCode rc, String msg) {
        return fail(rc, null, msg);
    }

    /**
     * 客户端错误
     */
    public static R fail(IResultCode rc, Object data, String msg) {
        return of(rc, data, msg);
    }

    /**
     * 值为null则返回404
     */
    public static R $404(Optional<?> optional) {
        return optional(optional, RC.NOT_FOUND);
    }

    /**
     * 值为null则返回404
     */
    public static R $404(Object o) {
        return $404(Optional.ofNullable(o));
    }

    /**
     * 值为null则返回自定义代码
     */
    public static R optional(Optional<?> optional, RC rc) {
        if (optional.isPresent()) {
            return ok(optional.get());
        } else {
            return of(rc, null, null);
        }
    }

    /**
     * 值为null则返回自定义代码
     */
    public static R optional(Object o, RC rc) {
        return optional(Optional.ofNullable(o), rc);
    }


    @JsonIgnore
    public boolean isSuccess() {
        return this.httpStatus.is2xxSuccessful();
    }

    public R set(String property, Object value) {
        if (this.params == null) {
            this.params = new HashMap<>();
        }
        this.params.put(property, value);
        return this;
    }

    public R setData(Object data) {
        this.data = data;
        return this;
    }

    public R setData(String property, Object data) {
        if (this.data == null) {
            setData(new HashMap<>());
        }
        if (this.data instanceof Map map) {
            map.put(property, data);
        } else {
            throw new RuntimeException("data 不是 Map");
        }
        return this;
    }

    public R encrypted(boolean encrypted) {
        this.encrypted = encrypted;
        return this;
    }

    public R encrypted() {
        return encrypted(true);
    }

    public ResponseEntity<R> toEntity() {
        return ResponseEntity.status(this.httpStatus).body(this);
    }

    public ApiException toException() {
        return new ApiException(this);
    }

}
